package app;

import datastructure.Instance;
import datastructure.Solution;

import java.util.Arrays;

public class VNSprocess {

    static int NK1_INDEX;
    static int NK2_INDEX;
    int divide = 0;
    int numofCustomer = 0;
    int numofPFC = 0;
    int numofTask = 0;
    double a = 0; // platoon saving factor
    double b = 0;// platoon saving factor
    double Len = 0; // maximum platoon length
    double c1 = 0; // cost driver
    double c2 = 0; //cost fuel
    double miu = 0; //cost fuel
    double Fi = 0;
    double Fm = 0;
    double[] delta;
    double[][] distanceMatrix;
    int[][] ODdemand;
    double[][] coordinateofPFC;
    double[][] coordinateofCustomer;
    double[][] coordinate;
    int experimentnumber = 0;
    String time;
    Instance instance;
    VNSprocess VNS;
    int[] InitialS;
    double Transcost = 0;
    int numofIter = 0;
    double[] LineChartData;
    int[] ODdemandSequ;
    int[][] ODflow;
    double[] CapaOD;
    double[] CapaODOPT;
    double CalCost;
    double CalCostOPT;
    int[][] IterMode;
    int[][] IterModeOPT;
    int[] FirstIter;
    int[] FirstIterOPT;
    double[] CapaODOPTpast;
    double CalCostOPTpast;
    int[][] IterModeOPTpast;
    int[] FirstIterOPTpast;
    int[][] MODErevise;

    //构造方法
    public VNSprocess(Instance instance, int experimentnumber, String time) {

        this.numofPFC = instance.numofPFC;
        this.divide = instance.divide;
        this.numofCustomer = instance.numofCustomer;
        this.numofTask = instance.numofTask;
        this.a = instance.a;// platoon saving factor
        this.b = instance.b; // platoon saving factor
        this.Len = instance.Len; // maximum platoon length
        this.c1 = instance.c1; // cost weight
        this.c2 = instance.c2;
        this.miu = instance.miu;
        this.Fi = instance.Fi; // cost weight
        this.Fm = instance.Fm; // cost weight
        this.delta = instance.delta;
        this.ODdemand = instance.ODdemand;
        this.distanceMatrix = instance.distanceMatrix;
        this.coordinateofPFC = instance.coordinateofPFC;
        this.coordinateofCustomer = instance.coordinateofCustomer;
        this.experimentnumber = experimentnumber;
        this.time = time;
        this.instance = instance;
    }


    public Solution Iter(VNSprocess vns) {
        this.VNS = vns;
        NK1_INDEX = 0;
        NK2_INDEX = 0;
        VNS.Flowarrangemant();
        GenerateInitial generateInitial = new GenerateInitial(instance, experimentnumber, time);
        this.InitialS = generateInitial.Initial(ODdemandSequ, ODflow);

        System.out.println();
        this.CapaOD = new double[numofPFC];
        this.CapaODOPT = new double[numofPFC];
        FirstIterOPT = InitialS.clone();
        FirstIter = InitialS.clone();

        CalCost = VNS.FlowCal();
        CalCostOPT = CalCost;
        CapaODOPTpast = CapaODOPT.clone();
        CalCostOPTpast = CalCostOPT;
        this.LineChartData = new double[1000 * divide * divide];
        LineChartData[numofIter] = CalCostOPT;
        numofIter++;
        double initialcost = CalCost;
        System.out.println("initial_value:" + initialcost);
        System.out.println("first_initial_solution");
        for (int i = 0; i < numofPFC; i++) {
            System.out.print((i + 1) + ":");
            System.out.print(InitialS[i]);
            System.out.print("//");
        }
        System.out.println();
        System.out.println("--------------------------------------");

        int Iter = 0;
        int K = 0;

//        int MaxIter = (int)(divide*divide/20);
        int MaxIter = (int) (divide / 20);
        int MaxK = 5;

        VNS.BVND();
        CapaODOPTpast = CapaODOPT.clone();
        CalCostOPTpast = CalCostOPT;
        IterModeOPTpast = IterModeOPT.clone();
        FirstIterOPTpast = FirstIterOPT.clone();

        System.out.println("first_VND_value:" + CalCostOPTpast);
        System.out.println("first_VND_solution");
        for (int i = 0; i < numofPFC; i++) {
            System.out.print((i + 1) + ":");
            System.out.print(FirstIterOPTpast[i]);
            System.out.print("//");
        }
        System.out.println();
        System.out.println("--------------------------------------");

        while (Iter <= MaxIter) {
            K = 1;
            while (K <= MaxK) {
                VNS.ShakingNeigh(K);
                VNS.BVND();
                K++;
                if (CalCostOPT < CalCostOPTpast) {
                    CapaODOPTpast = CapaODOPT.clone();
                    CalCostOPTpast = CalCostOPT;
                    IterModeOPTpast = IterModeOPT.clone();
                    FirstIterOPTpast = FirstIterOPT.clone();
                    K = 1;
                    Iter = 0;
                    LineChartData[numofIter] = CalCostOPTpast;
                    numofIter++;
                } else {
                    CapaODOPT = CapaODOPTpast.clone();
                    CalCostOPT = CalCostOPTpast;
                    IterModeOPT = IterModeOPTpast.clone();
                    FirstIterOPT = FirstIterOPTpast.clone();
                    CapaOD = CapaODOPTpast.clone();
                    CalCost = CalCostOPTpast;
                    IterMode = IterModeOPTpast.clone();
                    FirstIter = FirstIterOPTpast.clone();
                    LineChartData[numofIter] = CalCostOPTpast;
                    numofIter++;
                }
            }
            Iter++;
        }

        System.out.println("********************************************************");
        System.out.println("initial_value:" + initialcost);
        System.out.println("********************************************************");
        System.out.println("final_value:" + CalCostOPTpast);
        System.out.println("********************************************************");
        System.out.println("NK1_improve" + NK1_INDEX);
        System.out.println("NK2_improve" + NK2_INDEX);

//        LineChart lineChart = new LineChart(instance);
//        lineChart.TimeSeriesChart(LineChartData , numofIter);
        Solution solution = new Solution();
        solution.FirstIterOPTpast = this.FirstIterOPTpast;
        solution.CapaODOPTpast = this.CapaODOPTpast;
        solution.IterModeOPTpast = this.IterModeOPTpast;
        solution.ODdemandSequ = this.ODdemandSequ;
        solution.ODflow = this.ODflow;
        solution.Transcost = this.Transcost;
        solution.MODErevise = this.MODErevise;
        ;
        return solution;
    }

    public void Flowarrangemant() {
        int[] ODsequence = new int[numofCustomer * numofCustomer];
        int[][] ODflowAll = new int[numofCustomer * numofCustomer][2];
        int index = 0;
        for (int i = 0; i < numofCustomer; i++) {
            for (int j = 0; j < numofCustomer; j++) {
                if (ODdemand[i][j] > 0) {
                    ODsequence[index] = ODdemand[i][j];
                    ODflowAll[index][0] = i;
                    ODflowAll[index][1] = j;
                    index++;
                }
            }
        }

        this.ODdemandSequ = new int[index];
        this.ODflow = new int[index][2];

        System.arraycopy(ODsequence, 0, ODdemandSequ, 0, index);
        System.arraycopy(ODflowAll, 0, ODflow, 0, index);

        this.IterModeOPT = new int[index][2];
        this.IterMode = new int[index][2];
        this.MODErevise = new int[index][2];

    }

    public double FlowCal() {
        double cost = 0;
        double[] mode;
        this.CapaOD = new double[numofPFC];
        for (int i = 0; i < ODdemandSequ.length; i++) {
            mode = VNS.TransMode(ODflow[i][0], ODflow[i][1]);
            cost = mode[2] + cost;
            IterMode[i][0] = (int) mode[0];
            IterMode[i][1] = (int) mode[1];
        }
        double temp2 = 0;
        temp2 = cost;
        for (int i = 0; i < numofPFC; i++) {
            if (FirstIter[i] > 0) {
                if (CapaOD[i] >= Fi) {
                    cost = cost + CapaOD[i] * delta[i];
                } else {
                    cost = cost + (Fi) * delta[i];
                }
            }
        }
        if (cost < CalCostOPTpast) {
            Transcost = temp2;
        }

        return cost;
    }

    public double[] TransMode(int temp1, int temp2) {
        double[] mode = new double[4];
        mode[2] = 10000000;
        mode[3] = 10000000;
        double TradCost = (c1 + c2 * miu) * ODdemand[temp1][temp2] * distanceMatrix[temp1][temp2];
        for (int i = 1; i < numofPFC + 1; i++) {
            for (int j = 1; j < numofPFC + 1; j++) {
                if (i != j && FirstIter[i - 1] > 0 && FirstIter[j - 1] > 0 && CapaOD[i - 1] + ODdemand[temp1][temp2] < Fm && CapaOD[j - 1] + ODdemand[temp1][temp2] < Fm) {
                    double PFCCost1 = (c1 + c2 * miu) * ODdemand[temp1][temp2] * (distanceMatrix[temp1][numofCustomer + i - 1] + distanceMatrix[numofCustomer + j - 1][temp2]) +
                            c1 * (ODdemand[temp1][temp2] / Len) * distanceMatrix[numofCustomer + i - 1][numofCustomer + j - 1] +
                            c2 * miu * (ODdemand[temp1][temp2] / Len) * ((1 - a) + (Len - 1) * (1 - b)) * distanceMatrix[numofCustomer + i - 1][numofCustomer + j - 1];
                    double PFCCost2 = 0;
                    PFCCost2 = PFCCost1 + ODdemand[temp1][temp2] * delta[i - 1] + ODdemand[temp1][temp2] * delta[j - 1];

                    if ((PFCCost2 < mode[3]) && (PFCCost2 < TradCost)) {
                        mode[0] = i;
                        mode[1] = j;
                        mode[2] = PFCCost1;
                        mode[3] = PFCCost2;
                    }
                }
            }
        }
        if (mode[0] == 0) {
            mode[2] = TradCost;
        } else {
            CapaOD[(int) mode[0] - 1] = CapaOD[(int) mode[0] - 1] + ODdemand[temp1][temp2];
            CapaOD[(int) mode[1] - 1] = CapaOD[(int) mode[1] - 1] + ODdemand[temp1][temp2];
        }

        return mode;

    }

    public int NK1() {
        double[] temp1 = new double[numofPFC];
        double[] temp2;
        double[] temp3 = CapaOD.clone();
        for (int i = 0; i < numofPFC; i++) {
            if (FirstIter[i] > 0) {
                temp1[i] = (CapaOD[i] - Fi) * delta[i];
            } else {
                temp3[i] = 10000;
            }
        }
        temp2 = temp1.clone();
        Arrays.sort(temp1);
        Arrays.sort(temp3);
        if (temp1[0] >= 0) {
            for (int i = 0; i < numofPFC; i++) {
                if (temp3[0] == CapaOD[i]) {
                    FirstIter[i] = 0;
                    break;
                }
            }
        } else {
            for (int i = 0; i < numofPFC; i++) {
                if (temp1[0] == temp2[i]) {
                    FirstIter[i] = 0;
                    break;
                }
            }
        }
        CalCost = VNS.FlowCal();

        if (CalCost < CalCostOPTpast) {
            NK1_INDEX++;
        }

        if (CalCost < CalCostOPT) {
            FirstIterOPT = FirstIter.clone();
            CalCostOPT = CalCost;
            CapaODOPT = CapaOD.clone();
            IterModeOPT = IterMode.clone();
            return 1;
        } else {
            FirstIter = FirstIterOPT.clone();
            CapaOD = CapaODOPT.clone();
            IterMode = IterModeOPT.clone();
            return 0;
        }

    }

    public int NK2() {
        double cost = CalCost;
        int[] temp = FirstIterOPT.clone();
        int index = 0;
//        for_uniform
//        for(int i = 0; i < divide + 1; i++){
//            for (int j = 0; j < divide + 1 ; j++) {
//                if(i == 0||j == 0||i == divide||j == divide){
//                    index++;
//                    continue;
//                }else{
//                    if(FirstIterOPT[index] == 0){
//                        FirstIter[index] = 1;
//                        CalCost = VNS.FlowCal();
//                        if(CalCost < cost){
//                            temp = FirstIter.clone();
//                            cost = CalCost;
//                        }
//                        FirstIter = FirstIterOPT.clone();
//                    }
//                    index++;
//                }
//            }
//        }

//        for_random
        for (int i = 0; i < divide; i++) {
            if (FirstIterOPT[i] == 0) {
                FirstIter[i] = 1;
                CalCost = VNS.FlowCal();
                if (CalCost < cost) {
                    temp = FirstIter.clone();
                    cost = CalCost;
                }
                FirstIter = FirstIterOPT.clone();
            }
        }

        if (CalCost < CalCostOPTpast) {
            NK2_INDEX++;
        }

        if (cost < CalCostOPT) {
            FirstIterOPT = temp.clone();
            CalCostOPT = cost;
            FirstIter = FirstIterOPT.clone();
            CapaODOPT = CapaOD.clone();
            IterModeOPT = IterMode.clone();
            return 1;
        } else {
            FirstIter = FirstIterOPT.clone();
            CapaOD = CapaODOPT.clone();
            IterMode = IterModeOPT.clone();
            return 0;
        }

    }

    public void ShakingNeigh(int T) {
        int t = 0;
        while (t < T) {
            double random = Math.random();
            if (random < 0.4) {
                double[] temp1 = new double[numofPFC];
                double[] temp2;
                double[] temp3 = CapaOD.clone();
                for (int i = 0; i < numofPFC; i++) {
                    if (FirstIter[i] > 0) {
                        temp1[i] = (CapaOD[i] - Fi) * delta[i];
                    } else {
                        temp3[i] = 10000;
                    }
                }
                temp2 = temp1.clone();
                Arrays.sort(temp1);
                Arrays.sort(temp3);
                if (temp1[0] >= 0) {
                    for (int i = 0; i < numofPFC; i++) {
                        if (temp3[0] == CapaOD[i]) {
                            FirstIter[i] = 0;
                            break;
                        }
                    }
                } else {
                    for (int i = 0; i < numofPFC; i++) {
                        if (temp1[0] == temp2[i]) {
                            FirstIter[i] = 0;
                            break;
                        }
                    }
                }
                CalCost = VNS.FlowCal();
                CapaODOPT = CapaOD.clone();
                CalCostOPT = CalCost;
                FirstIterOPT = FirstIter.clone();
                IterModeOPT = IterMode.clone();
                LineChartData[numofIter] = CalCostOPT;
                numofIter++;

            } else {
                double cost = CalCost * 100;
                int[] temp = FirstIterOPT.clone();
                int index = 0;
//                for_uniform
//                for(int i = 0; i < divide + 1; i++){
//                    out:for (int j = 0; j < divide + 1 ; j++) {
//                        if(i == 0||j == 0||i == divide||j == divide){
//                            continue out;
//                        }else{
//                            if(FirstIterOPT[index] == 0){
//                                FirstIter[index] = 1;
//                                CalCost = VNS.FlowCal();
//                                if(CalCost < cost){
//                                    temp = FirstIter.clone();
//                                    cost = CalCost;
//                                }
//                                FirstIter = FirstIterOPT.clone();
//                            }
//                        }
//                        index++;
//                    }
//                }

//                for_random
                for (int i = 0; i < divide; i++) {
                    if (FirstIterOPT[i] == 0) {
                        FirstIter[i] = 1;
                        CalCost = VNS.FlowCal();
                        if (CalCost < cost) {
                            temp = FirstIter.clone();
                            cost = CalCost;
                        }
                        FirstIter = FirstIterOPT.clone();
                    }
                }

                FirstIterOPT = temp.clone();
                CalCostOPT = cost;
                FirstIter = FirstIterOPT.clone();
                CapaODOPT = CapaOD.clone();
                IterModeOPT = IterMode.clone();
                LineChartData[numofIter] = CalCostOPT;
                numofIter++;
            }

            System.out.println("VNS_iter_value:" + CalCostOPT);
            System.out.println("VNS_iter_solution");
            for (int i = 0; i < numofPFC; i++) {
                System.out.print((i + 1) + ":");
                System.out.print(FirstIterOPT[i]);
                System.out.print("//");
            }
            System.out.println();

            t++;
        }

    }

    public void BVND() {
        int k = 1;
        while (k <= 2) {
            int temp = -1;
            if (k == 1) {
                k = 2;
                temp = VNS.NK2();

            } else if (k == 2) {
                k = 3;
                temp = VNS.NK1();

            }
            LineChartData[numofIter] = CalCostOPT;
            numofIter++;
            if (temp == 1) {
                k = 1;
            }
        }

    }


}
